using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace RStein.Posterous.API
{
    internal class PosterousSite : EntityWithClientStateBase<PosterousSite>, IPosterousSite
    {
        #region constants
        public readonly int MAX_PAGE_POST_SIZE = 50;
        public readonly int START_POST_PAGE = 1;
        public readonly int PAGE_POST_INCREMENT= 1;
        #endregion constants

        #region Constructors

        public PosterousSite(int id) : base(id, PosterousEntityState.Loaded)
        {
            m_posts = new List<IPosterousPost>();
            m_postsLockRoot = new Object();
            m_tagsLockRoot = new Object();
            m_tags = Enumerable.Empty<string>();
        }

        #endregion Constructors
        #region private variables

        private List<IPosterousPost> m_posts;
        private  PosterousAccount m_posterousAccount;
        private object m_postsLockRoot;
        private object m_tagsLockRoot;
        private IEnumerable<string> m_tags;

        #endregion private variables
        #region Methods
        protected void OnPostInPageLoaded(EventArgsValue<IEnumerable<IPosterousPost>> e)
        {
            EventHandler<EventArgsValue<IEnumerable<IPosterousPost>>> handler = PostsInPageLoaded;
            if (handler != null)
            {
                Thread.CurrentThread.GetCurrentSynchronizationContext().Post(o => handler(this, e), null);
            }
        }

        protected void OnPostsLoaded(EventArgsValue<IEnumerable<IPosterousPost>> e)
        {
            EventHandler<EventArgsValue<IEnumerable<IPosterousPost>>> handler = PostsLoaded;
            if (handler != null)
            {
                Thread.CurrentThread.GetCurrentSynchronizationContext().Post(o => handler(this, e), null);
            }
        }

        protected void OnTagsLoaded(EventArgsValue<IEnumerable<string>> e)
        {
            EventHandler<EventArgsValue<IEnumerable<string>>> handler = TagsLoaded;
            if (handler != null)
            {
                Thread.CurrentThread.GetCurrentSynchronizationContext().Post(o => handler(this, e), null);
            }
        }

        protected void OnPostsByTagLoaded(EventArgsValue<IEnumerable<IPosterousPost>> e)
        {
            EventHandler<EventArgsValue<IEnumerable<IPosterousPost>>> handler = PostsByTagLoaded;
            if (handler != null)
            {
                Thread.CurrentThread.GetCurrentSynchronizationContext().Post(o => handler(this, e), null);
            }        
            
        }

        private IPosterousRequestBuilder getReqBuilder(int? startPage, int? recordsCount)
        {
            var reqBuilder = SimpleWebFactory.Instance.Create<IPosterousRequestBuilder>(); ;
            reqBuilder.StartBuildWebRequest(GlobalConstants.URL_READ_POSTS);
            AddSiteAuthInfo(reqBuilder);            
            reqBuilder.BuildFormData(GlobalConstants.SITE_REQ_ID, Id.ToString());
            

            if (recordsCount != null)
            {
                reqBuilder.BuildFormData(GlobalConstants.SITE_REQ_POSTSNUMBER, ((int)recordsCount).ToString());
            }
            
            if (startPage != null)
            {
                reqBuilder.BuildFormData(GlobalConstants.SITE_REQ_PAGE_NUMBER, ((int)startPage).ToString());    
            }
            
            return reqBuilder;
        }

        private bool isLoadNeeded()
        {
            return ((m_posts.Count() == 0) && (TotalPosts != 0));
        }

        private IEnumerable<IPosterousPost> getResponsePosts(IPosterousRequestBuilder reqBuilder)
        {
            var response = reqBuilder.GetResponse();
            var responseBuilder = new PosterousResponseBuilder();
            return responseBuilder.ParseResponse(response, new PostResponseParser().GetResults);
        }

        private void addLoadedPosts(IEnumerable<IPosterousPost> posts, bool clearCollection)
        {
            lock(m_postsLockRoot)
            {
                
                int oldPostsCount = m_posts.Count;
                posts.Cast<PosterousPost>().ToList().ForEach(post => post.Site = this);

                if (clearCollection)
                {
                    m_posts.Clear();
                }
                m_posts.AddRange(posts.Except(m_posts));

                if (m_posts.Count > oldPostsCount)
                {
                    OnPostsLoaded(new EventArgsValue<IEnumerable<IPosterousPost>>(m_posts, null));
                }    
            }
            
        }

        protected override void SaveUpdated()
        {
            var changedPosts = m_posts.Where(post => post.State == PosterousEntityState.New ||
                               post.State == PosterousEntityState.Updated)
                              .ToList();
            
            changedPosts.ForEach(post => post.SaveChanges());
          
        }

        protected override void SaveNew()
        {         
        }
        
        #endregion Methods
        #region Implementation of IExtensionInterface
        
        #endregion

        #region Implementation of IPosterousSite
        
        public string Name
        {
            get; 
            set;
        }        

        public string Url
        {
            get; 
            set;
        }        

        public bool IsPrivate
        {
            get; 
            set;
        }        

        public bool IsPrimary
        {
            get; 
            set;
        }        

        public bool AreCommentsEnabled
        {
            get;
            set;
        }

        public IPosterousAccount PosterousAccount
        {
            get
            {
                return m_posterousAccount;
            } 
            set
            {
                m_posterousAccount = value as PosterousAccount;
            }
        }


        public IEnumerable<IPosterousPost> Posts
        {
            get
            {
                if(isLoadNeeded())
                {
                    LoadAllPosts();
                }
                
                return m_posts;
            }            

        }

        

        public int TotalPosts
        {
            get;
            set;
        }

        public int LoadedPosts
        {
            get
            {
                return Posts.Count();
            }            
        }

        public void LoadAllPosts()
        {
         
            var tempPosts = new List<IPosterousPost>();            

            for (int startPage = START_POST_PAGE; tempPosts.Count < TotalPosts; startPage++)
            {
                IPosterousRequestBuilder reqBuilder = getReqBuilder(startPage, MAX_PAGE_POST_SIZE);
                IEnumerable<IPosterousPost> posts = getResponsePosts(reqBuilder);
                
                if (posts.Count() == 0)
                {
                    break;
                }

                tempPosts = tempPosts.Concat(posts).ToList();
            }
          
            addLoadedPosts(tempPosts, true);            
        }

        


        public void LoadAllPostsAsync()
        {
            Action<Exception> exchandler = e =>
                                 {
                                     new PosterousException(GlobalConstants.ASYNC_EXCEPTION_TEXT, e);
                                     OnPostsLoaded(new EventArgsValue<IEnumerable<IPosterousPost>>(null, e));
                                 };
            
            AsyncHelper.RunDelegateAsync((Action) LoadAllPosts, exchandler, null);            
        }

        public event EventHandler<EventArgsValue<IEnumerable<IPosterousPost>>> PostsLoaded;


        public IEnumerable<string> Tags
        {
            get
            {
                if (!IsTagsLoaded)
                {
                    LoadTags();
                }
                return m_tags;
            }
            set
            {
                m_tags = value;
            }
        }

        public bool IsTagsLoaded
        {
            get; 
            private set;
        }

        public void LoadTags()
        {
            var reqBuilder = SimpleWebFactory.Instance.Create<IPosterousRequestBuilder>();
            reqBuilder.StartBuildWebRequest(GlobalConstants.URL_SITES_GET_TAGS);
            AddSiteAuthInfo(reqBuilder);
            reqBuilder.BuildFormData(GlobalConstants.SITE_REQ_ID, Id.ToString());
            var response = reqBuilder.GetResponse();
            var respBuilder = new PosterousResponseBuilder();
            var tags = respBuilder.ParseResponse(response,
                                                new TagsResponseBuilder().GetResults).ToList();

            lock(m_tagsLockRoot)
            {
                Tags = tags;
                IsTagsLoaded = true;               
                OnTagsLoaded(new EventArgsValue<IEnumerable<string>>(tags, null));
            }
            
        }

        public void LoadTagsAsync()
        {
            Action<Exception> exchandler = e =>
            {
                var ex = new PosterousException(GlobalConstants.ASYNC_EXCEPTION_TEXT, e);
                OnTagsLoaded(new EventArgsValue<IEnumerable<string>>(null, ex));
            };
            
            AsyncHelper.RunDelegateAsync((Action) LoadTags, exchandler, null);                        
        }

        public event EventHandler<EventArgsValue<IEnumerable<string>>> TagsLoaded;
        

        public IEnumerable<IPosterousPost> GetPostsByTag(string tag)
        {
            if (String.IsNullOrEmpty(tag))
            {
                throw new ArgumentException("tag");
            }

            var builder = getReqBuilder(null, null);
            builder.BuildFormData(GlobalConstants.SITE_REQ_TAG, tag);
            var posts = getResponsePosts(builder);
            addLoadedPosts(posts, false);

            OnPostsByTagLoaded(new EventArgsValue<IEnumerable<IPosterousPost>>(posts, null));
            return m_posts;
        }

        

        public void GetPostsByTagAsync(string tag)
        {

            Action<Exception> exchandler = e =>
            {
                var ex = new PosterousException(GlobalConstants.ASYNC_EXCEPTION_TEXT, e);
                OnPostsByTagLoaded(new EventArgsValue<IEnumerable<IPosterousPost>>(null, ex));
            };

            AsyncHelper.RunDelegateAsync((Func<string, IEnumerable<IPosterousPost>>) GetPostsByTag, exchandler, null, tag);                  
        }

        public event EventHandler<EventArgsValue<IEnumerable<IPosterousPost>>> PostsByTagLoaded;

        
        public IEnumerable<IPosterousPost> GetPostsInPage(int page, int recordsCount)
        {
            int usedPage = page < START_POST_PAGE 
                                ? START_POST_PAGE 
                                : page;

            int usedRecordsCount = (recordsCount < 0 || recordsCount > MAX_PAGE_POST_SIZE)
                                         ? MAX_PAGE_POST_SIZE
                                         : recordsCount;

            var builder = getReqBuilder(usedPage, usedRecordsCount);
            var posts = getResponsePosts(builder);
            
            addLoadedPosts(posts, false); 

            OnPostInPageLoaded(new EventArgsValue<IEnumerable<IPosterousPost>>(posts, null));
            return m_posts;
        }

        public void GetPostsInPageAsync(int page, int recordsCount)
        {
            Action<Exception> exchandler = e =>
            {
                var ex = new PosterousException(GlobalConstants.ASYNC_EXCEPTION_TEXT, e);
                OnPostInPageLoaded(new EventArgsValue<IEnumerable<IPosterousPost>>(null, ex));
            };

            AsyncHelper.RunDelegateAsync((Func<int, int, IEnumerable<IPosterousPost>>) GetPostsInPage, exchandler, null, page, recordsCount);                              
        }

        public event EventHandler<EventArgsValue<IEnumerable<IPosterousPost>>> PostsInPageLoaded;
        public IPosterousPost CreatePost(string title, string body, bool autopostAll)
        {
            var post = new PosterousPost(this)
                           {
                               Title = title ?? String.Empty,
                               Body = body ?? String.Empty,
                               AutoPost = autopostAll,
                               PostDate = DateTime.Now
                               
                           };

            m_posts.Add(post);
            SetDirty();
            return post;
        }

        internal virtual void AddSiteAuthInfo(IPosterousRequestBuilder reqBuilder)
        {
            reqBuilder.BuildHttpAuthentication(m_posterousAccount.Name, m_posterousAccount.Password);
        }
        #endregion
    }
}